
package com.kugou.game.sdk.utils;

import com.kugou.framework.base.LogUtil;
import com.kugou.framework.http.KGHttpClient;
import com.kugou.framework.http.KGHttpClient.IDownloadListener;
import com.kugou.framework.http.RequestPackage;
import com.kugou.framework.utils.BitmapUtil;
import com.kugou.framework.utils.SDCardUtil;
import com.kugou.game.sdk.protocol.HttpBaseRequestPackage;

import org.apache.http.HttpEntity;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 描述:异步加载图片
 * 
 * @author chenys
 * @since 2013-9-25 下午5:42:30
 */
public class AsyncImageLoader {

    private final static String TAG = "AsyncImageLoader";

    private ThreadPoolExecutor mThreadPoolExecutor;

    // 最大bitmap缓存数
    private static final int MAX_CACHE_SIZE = 20;

    // 已加载好的图片缓存
    @SuppressWarnings("serial")
    private HashMap<String, SoftReference<Bitmap>> mBitmapCache = new LinkedHashMap<String, SoftReference<Bitmap>>(
            MAX_CACHE_SIZE, 0.75f, true);

    // 图片下载任务队列
    private LinkedHashMap<String, ImageCallback> mImageLoadingTasks = new LinkedHashMap<String, ImageCallback>(
            0);

    // 被临时取消的任务
    // private ArrayBlockingQueue<Runnable> mCancelTasks = new
    // ArrayBlockingQueue<Runnable>(100);

    private RejectedExecutionHandler mRejectedExecutionHandler = new RejectedExecutionHandler() {

        @Override
        public void rejectedExecution(final Runnable r, ThreadPoolExecutor executor) {
            if (!executor.isShutdown()) {
                BlockingQueue<Runnable> queue = executor.getQueue();
                // 从队列顶部删除任务
                final Runnable cancelRunnable = queue.poll();

                if (cancelRunnable instanceof ThreadPoolTask) {
                    String key = ((ThreadPoolTask) cancelRunnable).key;
                    mImageLoadingTasks.remove(key);
                    Log.d("tag", "---rejected task. " + key);

                    // 添加新的Runnable进队列
                    boolean b = queue.offer(r);
                    if (!b) {
                        // 从ImageCallback队列中移除对应的没添加成功的item
                        String key1 = ((ThreadPoolTask) r).key;
                        mImageLoadingTasks.remove(key1);
                    }

                    // if (cancelRunnable != null &&
                    // !mCancelTasks.contains(cancelRunnable)) {
                    // mCancelTasks.add(cancelRunnable);
                    // }
                    // executor.execute(r);
                }

            }

        }
    };

    // 图片下载完毕后刷新UI
    private Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message message) {
            Bitmap bitmap = message.obj == null ? null : (Bitmap) message.obj;
            String key = message.getData().getString("key");
            ImageCallback imageCallback = mImageLoadingTasks.get(key);
            if (imageCallback != null) {
                imageCallback.imageLoaded(bitmap, key);
            }
            mImageLoadingTasks.remove(key);
            Log.d("load img sucess", key);
        }
    };

    public AsyncImageLoader() {
        mThreadPoolExecutor = new ThreadPoolExecutor(1, 2, 3, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(40), mRejectedExecutionHandler);
        // mThreadPoolExecutor = CustomThreadPoolExecutor.getInstance(2, 3, 3,
        // TimeUnit.SECONDS,
        // new ArrayBlockingQueue<Runnable>(10),
        // Executors.defaultThreadFactory(),
        // mRejectedExecutionHandler);
    }

    // public void clearBitmap() {
    // for (int i = 0; i < mBitmapCache.size(); i++) {
    // SoftReference<Bitmap> softBitmap = mBitmapCache.get(i);
    // if (softBitmap != null) {
    // Bitmap bitmap = softBitmap.get();
    // if (bitmap != null && !bitmap.isRecycled()) {
    // bitmap.recycle();
    // bitmap = null;
    // }
    // }
    // }
    // System.gc();
    // }

    public void clearBitmap() {
        synchronized (mBitmapCache) {
            Iterator<Entry<String, SoftReference<Bitmap>>> iter = mBitmapCache.entrySet()
                    .iterator();
            SoftReference<Bitmap> softBitmap = null;
            while (iter.hasNext()) {
                Entry<String, SoftReference<Bitmap>> entry = iter.next();
                softBitmap = entry.getValue();
                if (softBitmap != null) {
                    Bitmap bitmap = softBitmap.get();
                    if (bitmap != null && !bitmap.isRecycled()) {
                        bitmap.recycle();
                        bitmap = null;
                    }
                }
            }
        }
        System.gc();
    }

    /**
     * 加载图片
     * 
     * @param context
     * @param key
     * @param imageurl
     * @param savePath
     * @param imageCallback
     * @return
     */
    public Bitmap loadImage(Context context, String key, String imageurl, String savePath,
            ImageCallback imageCallback) {
        return loadImage(context, key, imageurl, savePath, imageCallback, -1, -1);
    }

    /**
     * 是否已经显示过SD卡未加载的提示
     */
    private boolean showNoSdcardToast = false;

    /**
     * 加载图片
     * 
     * @param context
     * @param key
     * @param imageurl
     * @param savePath
     * @param imageCallback
     * @param width 显示宽度 -1时不裁剪
     * @param height 显示高度 -1时不裁剪
     * @return
     */
    public Bitmap loadImage(Context context, String key, String imageurl, String savePath,
            ImageCallback imageCallback, int width, int height) {
        if (TextUtils.isEmpty(key)) {
            return null;
        }

        // 先检查是否有缓存
        Bitmap bitmap = getBitmapFromCache(imageurl);
        if (bitmap != null) {
            return bitmap;
        }

        if (TextUtils.isEmpty(savePath)) {
            return null;
        }

        if (!SDCardUtil.hasSDCard()) {
            if (!showNoSdcardToast) {
                ToastUtil.showMsg("SD卡未挂载");
                showNoSdcardToast = true;
            }
            return null;
        }
        if (!SDCardUtil.hasEnoughSpace(1 * 1024 * 1024)) {
            ToastUtil.showMsg("SD卡空间已满，无法加载更多内容");
            return null;
        }

        // 开始下载
        if (!mImageLoadingTasks.containsKey(key)) {
            Log.d("key", key);
            Log.d("count", mImageLoadingTasks.size() + "");
            addToDownloadPool(key, imageurl, savePath, imageCallback, width, height);
        } else {
            LogUtil.d(TAG, "loading:" + key);
        }

        return null;
    }

    public boolean containsTask(String key) {
        return mImageLoadingTasks != null && mImageLoadingTasks.containsKey(key);
    }

    private synchronized void addToDownloadPool(String key, String imageurl, String savePath,
            ImageCallback imageCallback, int width, int height) {
        if (TextUtils.isEmpty(key) || TextUtils.isEmpty(imageurl)) {
            return;
        }
        mImageLoadingTasks.put(key, imageCallback);
        ThreadPoolTask threadPoolTask = new ThreadPoolTask(key, imageurl, savePath, width, height);
        // Log.d("count", mThreadPoolExecutor.getTaskCount() + "");
        mThreadPoolExecutor.execute(threadPoolTask);

    }

    /**
     * Clears the image cache used internally to improve performance. Note that
     * for memory efficiency reasons, the cache will automatically be cleared
     * after a certain inactivity delay.
     */
    public void clearCache() {
        clearBitmap();
        mThreadPoolExecutor.shutdownNow();
        mBitmapCache.clear();
        mImageLoadingTasks.clear();
    }

    private class ThreadPoolTask implements Runnable {

        String key;

        String imageurl;

        String savePath;

        int width;

        int height;

        public ThreadPoolTask(String key, String imageurl, String savePath, int width, int height) {
            this.key = key;
            this.imageurl = imageurl;
            this.savePath = savePath;
            this.width = width;
            this.height = height;
        }

        @Override
        public void run() {
            // 检查本地图片是否缓存
            Log.d("exe", key);
            File file = new File(savePath);
            if (file.exists() && file.length() > 0) {
                Bitmap bitmap;
                if (width > 0 && height > 0) {
                    bitmap = BitmapUtil.decodeFile(savePath, width, height);
                } else {
                    bitmap = BitmapUtil.decodeFile(savePath);
                }
                if (bitmap != null) {
                    LogUtil.d(TAG, "getBitmapFromSdcard.");
                    addBitmapToCache(imageurl, bitmap);
                    // 刷新UI
                    refreshUi(key, bitmap);
                    return;
                }
            }

            // 下载图片
            Bitmap bitmap = loadImageFromUrl(imageurl, savePath, width, height);
            if (bitmap != null) {
                addBitmapToCache(imageurl, bitmap);
                // 刷新UI
                refreshUi(key, bitmap);
                LogUtil.d(TAG, "loadImageFromUrl success.");
            } else {
                handler.post(new Runnable() {

                    @Override
                    public void run() {
                        mImageLoadingTasks.remove(key);
                        LogUtil.d(TAG, "loadImageFromUrl faile.");
                    }

                });
            }

            // if (!mThreadPoolExecutor.isShutdown()) {
            // if (mThreadPoolExecutor.getQueue().size() == 0 &&
            // mCancelTasks.size() > 0) {
            // mThreadPoolExecutor.execute(mCancelTasks.poll());
            // }
            // }
        }
    }

    private void refreshUi(String key, Bitmap bitmap) {
        Message message = handler.obtainMessage();
        message.obj = bitmap;
        Bundle bundle = new Bundle();
        bundle.putString("key", key);
        message.setData(bundle);
        handler.sendMessage(message);
    }

    private Bitmap getBitmapFromCache(String imageurl) {
        synchronized (mBitmapCache) {
            final SoftReference<Bitmap> softBitmap = mBitmapCache.get(imageurl);
            if (softBitmap != null) {
                Bitmap bitmap = softBitmap.get();
                if (bitmap != null && !bitmap.isRecycled()) {
                    LogUtil.d(TAG, "getBitmapFromCache.");
                    // Bitmap found in hard cache
                    // Move element to first position, so that it is removed
                    // last
                    mBitmapCache.remove(imageurl);
                    mBitmapCache.put(imageurl, softBitmap);
                    return bitmap;
                } else {
                    mBitmapCache.remove(imageurl);
                }
            }
        }

        return null;
    }

    private void addBitmapToCache(String imageurl, Bitmap bitmap) {
        synchronized (mBitmapCache) {
            mBitmapCache.put(imageurl, new SoftReference<Bitmap>(bitmap));
        }
    }

    /**
     * 从网络下载图片，下载成功保存在SD卡
     * 
     * @param url
     * @param savePath
     * @param width -1时不裁剪
     * @param height -1时不裁剪
     * @return
     */
    public Bitmap loadImageFromUrl(String url, String savePath, int width, int height) {
        if (TextUtils.isEmpty(url) || TextUtils.isEmpty(savePath)) {
            return null;
        }
        boolean isDownloadError = false;
        String savePathTemp = savePath + ".tmp";
        ImageRequestPackage req = new ImageRequestPackage(url);
        ImageDownloadListener listener = new ImageDownloadListener(savePathTemp);
        try {
            KGHttpClient.download(req, listener);
        } catch (Exception e) {
            FileUtil.deleteFile(savePathTemp);
            return null;
        }
        isDownloadError = listener.isError();
        if (isDownloadError) {
            FileUtil.deleteFile(savePathTemp);
            return null;
        }
        FileUtil.rename(savePathTemp, savePath);
        Bitmap bitmap;
        if (mHandleBitmap != null) {
            bitmap = mHandleBitmap.doWithNetBitmap(null, savePath);
        } else if (width > 0 && height > 0) {
            bitmap = BitmapUtil.decodeFile(savePath, width, height);
        } else {
            bitmap = BitmapUtil.decodeFile(savePath);
        }
        return bitmap;
    }

    private IHandleNetBitmap mHandleBitmap;

    public void setHandleNetBitmap(IHandleNetBitmap listener) {
        mHandleBitmap = listener;
    }

    /**
     * 描述:下载图片后，对图片进行处理（例如：等比例放大缩小）
     * 
     * @author chenys
     * @since 2013-10-31 上午11:29:47
     */
    public interface IHandleNetBitmap {
        Bitmap doWithNetBitmap(Bitmap srcBitmap, String srcPath);
    }

    public interface ImageCallback {

        public void imageLoaded(Bitmap bitmap, String key);
    }

    static class ImageRequestPackage extends HttpBaseRequestPackage {

        private String reqUrl;

        public ImageRequestPackage(String url) {
            this.reqUrl = url;
        }

        @Override
        public String getGetRequestParams() {
            return "";
        }

        @Override
        public HttpEntity getPostRequestEntity() {
            return null;
        }

        @Override
        public String getUrl() {
            return reqUrl;
        }

        @Override
        public int getRequestType() {
            return RequestPackage.TYPE_GET;
        }

    }

    static class ImageDownloadListener implements IDownloadListener {

        private String savePath;

        private FileOutputStream fos;

        private boolean isError;

        ImageDownloadListener(String savePath) {
            this.savePath = savePath;
        }

        @Override
        public void onProgressChanged(byte[] data, int offset, int length, int progress) {
            try {
                if (fos == null) {
                    File file = new File(savePath);
                    if (!file.exists()) {
                        File parent = file.getParentFile();
                        if (!parent.exists()) {
                            parent.mkdirs();
                        }
                        file.createNewFile();
                    }
                    this.fos = new FileOutputStream(file);
                }
                fos.write(data, offset, length);
            } catch (Exception e) {
                isError = true;
                e.printStackTrace();
            }
        }

        boolean isError() {
            return isError;
        }

    }

}
